﻿using System;
using System.Collections.Generic;
using System.Windows.Input;
using Projecturo.ProjecturoClient.Command;
using Projecturo.ProjecturoClient.Connection;
using Projecturo.Model;
using System.Threading;
using System.Reflection;
using Neits.Common;
using Neits.PersistenceService.Providers;

namespace Projecturo.ProjecturoClient.ViewModel.Project
{
    public class ProjectViewModel : WorkspaceViewModel
    {
        private Model.Project project;
        private Int64 stateId;
        private IList<TicketState> ticketStates;
        private DelegateCommand<long> newTicketCommand;
        private DelegateCommand<long> openProjectCommand;
        private DelegateCommand<object> openTicketCommand;
        private DelegateCommand openProjectManagementCommand;

        public ProjectViewModel()
        {            
            project = new Model.Project {Name = "New Project", CreateDate = DateTime.Now};
        }

        public ProjectViewModel(Model.Project project) : this()
        {
            if (project.Id > 0)
            {
                this.project = project;
                base.DisplayName = project.Name;
            }
        }

        public override string DisplayName
        {
            get
            {
                return project.Name;
            }
            protected set
            {
                base.DisplayName = value;
                OnPropertyChanged("DisplayName");
            }
        }

        private Model.Project Project
        {
            get { return project; }
            set { project = value; }
        }

        public IList<TicketState> AvailableTicketStates
        {
            get
            {
                if (ticketStates == null)
                    ticketStates = TicketStateHelper.LoadTicketStates(Project, null, false);
                return ticketStates;
            }
        }

        public Int64 StateId
        {
            get
            {
                if (Project.DefaultTicketState != null)
                    stateId = Project.DefaultTicketState.Id;
                return stateId;
            }
            set
            {
                if (stateId == value)
                    return;
                stateId = value;
                UpdateTicketState();
                OnPropertyChanged("StateId");
            }
        }

        public Int32 PlannedEffortHours
        {
            get
            {
                Int32 plannedEffort = 0;
                if (project != null && project.Tickets.Count > 0)
                {
                    foreach (Ticket ticket in project.Tickets)
                    {
                        plannedEffort += ticket.ExpectedEffortHours;
                    }
                }
                return plannedEffort;
            }   
        }

        public string SpentEffortHours
        {
            get
            {
                var spendTime = new TimeSpan();
                if (project != null && project.Tickets.Count > 0)
                {
                    foreach (Ticket ticket in project.Tickets)
                    {
                        foreach (TimeRecord record in ticket.TimeRecords)
                        {
                            TimeSpan tempSpan = record.EndDate - record.StartDate;
                            spendTime = spendTime.Add(tempSpan);
                        }
                    }
                }
                return String.Format("{0} Hours {1} Minutes", spendTime.Hours, spendTime.Minutes);
            }
        }

        private void UpdateTicketState()
        {
            if (stateId > 0)
            {
                foreach (TicketState state in AvailableTicketStates)
                {
                    if (state.Id == stateId)
                    {
                        Project.DefaultTicketState = state;
                        break;
                    }
                }
            }
        }

        public ICommand NewTicketCommand
        {
            get
            {
                if (newTicketCommand == null)
                {
                    newTicketCommand = new DelegateCommand<long>(NewTicket, CanNewTicket);
                }
                return newTicketCommand;
            }
        }

        public ICommand OpenProjectCommand
        {
            get
            {
                if (openProjectCommand == null)
                    openProjectCommand = new DelegateCommand<long>(OpenProject);
                return openProjectCommand;
            }
        }
        
        public ICommand OpenTicketCommand
        {
            get
            {
                if (openTicketCommand == null)
                {
                    openTicketCommand = new DelegateCommand<object>(OpenTicket);
                }
                return openTicketCommand;
            }
        }

        private void OpenTicket(object ticketToOpen)
        {
            var instanceIdentifier = ticketToOpen as IInstanceIdentifier;
            if (instanceIdentifier != null)
            {
                ViewModelBase viewModel = ViewModelManager.Instance.CreateViewModel(typeof(Ticket), this);
                viewModel.SetModel(instanceIdentifier.Id);
                AddToWorkspace(viewModel as WorkspaceViewModel, ViewModelReference, viewModel as WorkspaceViewModel);
            }
        }

        public ICommand OpenProjectAdministrationCommand
        {
            get
            {
                if (openProjectManagementCommand == null)
                    openProjectManagementCommand = new DelegateCommand(OpenProjectManagement, CanOpenProjectManagement);
                return openProjectManagementCommand;
            }
        }

        private void OpenProjectManagement()
        {
            var viewModel = ViewModelManager.Instance.CreateViewModel("ProjectAdministration", this);
            viewModel.SetModel(Project);
            AddToWorkspace(viewModel as WorkspaceViewModel, ViewModelReference, viewModel as WorkspaceViewModel);
        }

        private bool CanOpenProjectManagement()
        {
            return Project != null && Project.Id > 0;
        }

        // TODO: PersistenceService has a problem when saving the current user (different context) - so this is a workaround
        protected override void Save()
        {
            base.Save();

            Project.ChangeDate = DateTime.Now;
            User currentUser = GetCurrentUser();

            bool newProject = Project.Id < 1;

            Connector.Connectivity.Storage.Save(Project);

            if (newProject)
            {
                var mapping = new UserProject {Project = Project, User = currentUser};
                Connector.Connectivity.Storage.Save(mapping);

                var filter = new HqlFilter("from TicketState ts where ts.Project is null");
                IList<TicketState> states = Connector.Connectivity.Storage.GetList<TicketState>(filter);
                foreach (TicketState state in states)
                {
                    TicketState newState = state.Clone();
                    newState.Project = project;
                    Connector.Connectivity.Storage.Save(newState);
                }
            }

            ViewModelBase parentViewModel = ViewModelManager.Instance.GetViewModel(ViewModelReference.ParentViewModelId);
            parentViewModel.Update();
        }

        private void NewTicket(long projectId)
        {
            ViewModelBase viewModelBase = ViewModelManager.Instance.CreateViewModel(typeof(Ticket), this);
            viewModelBase.SetParent(Project);
            AddToWorkspace(viewModelBase as WorkspaceViewModel, ViewModelReference, viewModelBase as WorkspaceViewModel);
        }

        private bool CanNewTicket(long projectId)
        {
            return Project.Id > 0;
        }

        private void LoadProject(long id)
        {
            Project = Connector.Connectivity.Storage.Get<Model.Project>(id);
        }

        private void OpenProject(long id)
        {
            LoadProject(id);
            AddToWorkspace(this, ViewModelReference, this);
        }

        private static User GetCurrentUser()
        {
            return Thread.CurrentPrincipal as User;
        }

        protected override bool CanSave()
        {
            return !(String.IsNullOrEmpty(Name) && String.IsNullOrEmpty(Description));
        }

        public Int64 Id
        {
            get { return Project.Id; }
        }

        public string Name 
        {
            get { return Project.Name; }
            set
            {
                if (Project.Name == value)
                    return;

                Project.Name = value;
                OnPropertyChanged("Name");
                OnPropertyChanged("DisplayName");
            }
        }

        public string Description
        {
            get { return Project.Description; }
            set
            {
                if (Project.Description == value)
                    return;

                Project.Description = value;
                OnPropertyChanged("Description");
            }
        }

        public IList<Ticket> Tickets
        {
            get
            {
                return Project.Tickets;
            }
        }

        public override void SetModel(long id)
        {
            LoadProject(id);
        }

        public override void SetModel(object model)
        {
            if (model is Model.Project)
                Project = model as Model.Project;
        }

        public override void Update()
        {
            if (Project != null && Project.Id > 0)
            {
                LoadProject(Project.Id);
                OnPropertyChanged("Tickets");
                OnPropertyChanged("PlannedEffortHours");
                OnPropertyChanged("SpentEffortHours");
            }
            base.Update();
        }

    }
}
